Skip to content

Minor optimization in shiftOut function #82

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
Sep 16, 2019
Merged

Minor optimization in shiftOut function #82

merged 2 commits into from
Sep 16, 2019

Conversation

luco5826
Copy link
Contributor

Hi!
I found this -very-little optimization in a function I was using with a 595 shift registers' project: while I was trying to understand what this function used to do, I figured out a (IMHO) cleaner solution.

Compiling an example with gcc -S generated a file 10-15 instruction lines shorter than the previous version, which could lead (i think) to a smaller workload for such a simple operation.

I didn't quite understand the double exclamation marks before the shift operation !!(val & ... so I don't know if this breaks something. Let me know!
Keep up with Arduino 'cause it's awesome!

Copy link
Collaborator

@matthijskooijman matthijskooijman left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This change looks good to me. AVR is notoriously bad in bitshifting (there is only a shift-by-one instruction), so doing it incrementally like this is a good way to help the compiler :-)

As for the !! those are two boolean-negation operators. By applying it twice, you effectively make a "convert to boolean" operator (e.g. 0 becomes false, non-0 becomes true). In this case, this helps to make sure either LOW/0/false or HIGH/1/true is passed to digitalWrite.

With your change, in the MSBFIRST case, you're passing 0 or 128 to digitalWrite. In practice, this will work, since digitalWrite for AVR treats all non-zero values as HIGH. Technically, the API (as documented on arduino.cc) only specifies LOW/HIGH (0/1) as valid values, so this violates the API. You could try adding it again (e.g. digitalWrite(dataPin, !!(val & 1))) but that probably adds a few instructions again :-)

I'm not really sure if the !! is really needed here. Even when not documented explicitly, the API of digitalWrite can be assumed to be 0/non-0 (especially since this call is inside the AVR core itself, so portability to other cores is not really an issue right now).

Summarizing: Looks good to merge as-is.

@luco5826
Copy link
Contributor Author

Hi @matthijskooijman thank you for your review!
I don't know why, but for a moment I tought that val & 128 would return 1 or 0, not 10000000 or 0. Maybe we could add the !! in the MSBFIRST in order to be consistent with the API, or maybe even (val >> 8) & 1 would do the trick but then it would be very similar to the old version!
Thank you again!

@bxparks
Copy link
Contributor

bxparks commented Apr 26, 2019

Good clean up. But it feels slightly wrong to depend on an undocumented API behavior, to allow (val & 128) to act as HIGH. For one thing, this could trigger a latent bug when someone refactors digitalWrite() in the future. Secondly, Arduino code can be copied into a non-AVR Core, which may not treat 128 as HIGH. Adding the double !! operator or the equivalent ((val & 128) !=0) (which ought to generate the exact same machine code if the compiler optimizes correctly), seems like the safer thing to do.

@luco5826
Copy link
Contributor Author

You're right, I'm gonna fix it and commit it again! Thank you for your fast reply!

@luco5826
Copy link
Contributor Author

Ok I fixed that, now digitalWrite will receive 1 or 0 both for LSBFIRST and MSBFIRST!

@facchinm facchinm self-requested a review April 29, 2019 07:55
Copy link
Collaborator

@matthijskooijman matthijskooijman left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@luco5826 did you check if the change added more instructions?

Looks good like this. It would be good to merge both commits, but I guess that can be done while merging.

@luco5826
Copy link
Contributor Author

Hi @matthijskooijman ! Right now I checked for the compiler result, there still is 13-15 lines of difference (under plain GCC).
I don't know about AVR compiler's optimization but I think this could be useful anyway!
Do you guys can test these changes with the AVR compiler?
Thanks for your approvals!

@luco5826
Copy link
Contributor Author

luco5826 commented May 7, 2019

Hi everyone!
I finally managed to check compiler's optimization under avr-gcc and found out that with the previous version 135 lines were generated against 118 with the second one, I know it's not that much but 17 instructions less on a basic operation is not bad!

@matthijskooijman
Copy link
Collaborator

Hey @luco5826, thanks for testing. However, I'm a bit confused. AFAICS there's three versions involved: The original, the optimized version without !! and the optimized version with !!. Which of these did you test?

You talk about the number of lines, is this the number of lines in the dissassembly output? Note that some instructions are 2 bytes and some are 4, so it might be better to compare the compiled code size instead (or, to do a local comparison, count bytes inside the shiftOut function)?

@luco5826
Copy link
Contributor Author

luco5826 commented May 9, 2019

Hi @matthijskooijman !
i tested the old version agiainst the last one I committed (the one with != 0)!
The output size I tested was the one generated by the -S option, which stands for

-S                       Compile only; do not assemble or link.

I don't know any other ways to test the differences, I tried to compile the code with avr-gcc located in arduino/hardware/tools/avr/bin with almost the same results as with plain gcc (in terms of code size)!

@aentinger aentinger merged commit 3883e49 into arduino:master Sep 16, 2019
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants